package codegen

import (
	"errors"
	"fmt"
	"gitee.com/hxchjm/goge/codegen/utils"
	"gitee.com/hxchjm/goge/codegen/utils/execx"
	"github.com/siddontang/go/config"
	"go/build"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
)

const apiTemplate = `// 定义项目 API 的 proto 文件 可以同时描述 gRPC 和 HTTP API
// protobuf 文件参考:
//  - https://developers.google.com/protocol-buffers/
syntax = "proto3";

//import "google/protobuf/empty.proto";
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
import "google/api/annotations.proto";

// package 命名使用 {appid}.{version} 的方式, version 形如 v1, v2 ..
package {{.serviceName}}.server.v1;

// NOTE: 最后请删除这些无用的注释 (゜-゜)つロ

option go_package = "./;api";
//option (gogoproto.goproto_getters_all) = false;

service {{.serviceCamelName}} {
  rpc Ping(EmptyReq) returns (EmptyRsp);
  rpc SayHello(HelloReq) returns (EmptyRsp);
  rpc SayHelloURL(HelloReq) returns (HelloRsp) {
    option (google.api.http) = {
      get: "/demo/say_hello"
    };
  };
}

message EmptyReq{}

message EmptyRsp{}

message HelloReq {
  string name = 1 [(gogoproto.moretags) = 'form:"name"'];
}

message HelloRsp {
  string Content = 1 ;
}
`

func genApi(rootDir, rootPkg string, cfg *config.Config, filename string) error {
	filename = path.Base(filename)
	svrName := path.Base(rootPkg)
	svrCamelName := utils.SnakeToCamel(path.Base(rootPkg))
	err := utils.GenFile(utils.FileGenConfig{
		Dir:          rootDir,
		Subdir:       apiDir,
		Filename:     filename,
		TemplateName: "mainTemplate",
		//Category:        category,
		//TemplateFile:    mainTemplateFile,
		BuiltinTemplate: apiTemplate,
		Data: map[string]string{
			"serviceName":      svrName,
			"serviceCamelName": svrCamelName,
		},
	})
	if err != nil {
		return err
	}

	if err = checkProtoc(); err != nil {
		return err
	}
	if err = checkGoFast(); err != nil {
		return err
	}
	if err = checkBm(); err != nil {
		return err
	}
	if err = genPb(filepath.Join(rootDir, "api"), filepath.Join(rootDir, "api", filename), []string{}); err != nil {
		return err
	}

	if err = genBm(filepath.Join(rootDir, "api"), filepath.Join(rootDir, "api", filename), []string{}); err != nil {
		return err
	}
	//protoFiles := []string{
	//	filepath.Join(dir, "api","api.proto"),
	//}
	//generate(_pbCmd,dir, protoFiles)
	return nil
}

func checkProtoc() error {
	if _, err := exec.LookPath("protoc"); err != nil {
		switch runtime.GOOS {
		case "darwin":
			fmt.Println("brew install protobuf")
			cmd := exec.Command("brew", "install", "protobuf")
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			if err = cmd.Run(); err != nil {
				return err
			}
		case "linux":
			fmt.Println("snap install --classic protobuf")
			cmd := exec.Command("snap", "install", "--classic", "protobuf")
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			if err = cmd.Run(); err != nil {
				return err
			}
		default:
			return errors.New("您还没安装protoc，请进行手动安装：https://github.com/protocolbuffers/protobuf/releases")
		}
	}
	return nil
}

func checkBm() error {
	if _, err := exec.LookPath("protoc-gen-dam"); err != nil {
		/*		path, err := latestGoge()
				if err != nil {
					return err
				}
				bmPath := filepath.Join(path, "plugins", "protoc-gen-dam")
				if runtime.GOOS == execx.OsWindows {
					bmPath += "\\."
				} else {
					bmPath += "/."
				}*/

		//bmInstallCmd:=fmt.Sprint("go install ",bmPath)
		_, err = execx.Run("go install gitee.com/hxchjm/protoc-gen-dam@master", "")
		if err != nil {
			fmt.Printf("cmd(%+v) run err(%+v)\n", "", err)
			return err
		}
		return err
	}
	return nil
}

func checkGoFast() error {
	goFastRepo := "go install github.com/gogo/protobuf/protoc-gen-gofast@latest"
	if _, err := exec.LookPath("protoc-gen-gofast"); err == nil {
		return nil
	}
	_, err := execx.Run(goFastRepo, "")
	if err != nil {
		fmt.Printf("cmd(%+v) run err(%+v)\n", goFastRepo, err)
		return err
	}

	return nil
}

//v2
//protoc api.proto --proto_path=./ --proto_path=D:/share/go_proj/goge/plugins/pkg/extensions --go_opt=Mgoogle/protobuf/descriptor.proto=D:/share/go_proj/goge/plugins/pkg/extensions  --go_out==plugins=grpc:.
//protoc api.proto --proto_path=./ --proto_path=D:/share/go_proj/goge/plugins/pkg/extensions --dam_out=.

// const _pbCmd = "protoc --proto_path=%s --proto_path=%s --go_opt=Mgoogle/protobuf/descriptor.proto=%s --go_out=plugins=grpc:%s %s"
const _pbCmd = "protoc %s --gofast_out=Mgoogle/protobuf/any.proto=github.com/gogo/protobuf/types,Mgoogle/protobuf/duration.proto=github.com/gogo/protobuf/types,Mgoogle/protobuf/struct.proto=github.com/gogo/protobuf/types,Mgoogle/protobuf/timestamp.proto=github.com/gogo/protobuf/types,Mgoogle/protobuf/wrappers.proto=github.com/gogo/protobuf/types,plugins=grpc:%s %s"
const _bmCmd = "protoc %s --dam_out=explicit_http=true:%s %s"

func genPb(dstDir, name string, includeDirs []string) error {
	//apiDir := filepath.Join(dir, "api")
	//apiFile := filepath.Join(dir, name)
	dstDir, _ = filepath.Abs(dstDir)
	currDir := filepath.Dir(name)
	ext, err := extensionPath()
	if err != nil {
		return err
	}

	var paths []string
	for _, v := range append(includeDirs, currDir, ext) {
		paths = append(paths, fmt.Sprintf(`--proto_path=%s`, v))
	}
	cmdStr := fmt.Sprintf(_pbCmd, strings.Join(paths, " "), dstDir, name)
	fmt.Println("GRPC: ", cmdStr)
	if _, err = execx.Run(cmdStr, ""); err != nil {
		return err
	}
	return nil
}

// dstDir-最终生成的目的文件夹
// name - proto文件路径
func genBm(dstDir, name string, includeDirs []string) error {
	//apiDir := filepath.Join(dir, "api")
	dstDir, _ = filepath.Abs(dstDir)
	currDir := filepath.Dir(name)
	//apiFile := filepath.Join(dir, name)
	ext, err := extensionPath()
	if err != nil {
		return err
	}

	var paths []string
	for _, v := range append(includeDirs, currDir, ext) {
		paths = append(paths, fmt.Sprintf(`--proto_path=%s`, v))
	}

	cmdStr := fmt.Sprintf(_bmCmd, strings.Join(paths, " "), dstDir, name)
	fmt.Println("Dam: ", cmdStr)
	if _, err = execx.Run(cmdStr, "./"); err != nil {
		return err
	}
	return nil
}

//
//func generate(protoc string,dir string, files []string) error {
//	pwd, _ := os.Getwd()
//	//gosrc := filepath.Join(gopath(), "src")
//	ext, err := latestGoge()
//	if err != nil {
//		return err
//	}
//	apiDir:=filepath.Join(dir,"api")
//	line := fmt.Sprintf(protoc, dir, ext, dir, apiDir)
//	log.Println(line, strings.Join(files, " "))
//	args := strings.Split(line, " ")
//	args = append(args, files...)
//	cmd := exec.Command(args[0], args[1:]...)
//	cmd.Dir = pwd
//	cmd.Env = os.Environ()
//	cmd.Stdout = os.Stdout
//	cmd.Stderr = os.Stderr
//	return cmd.Run()
//}

const commonPath = "gitee.com/hxchjm"

func extensionPath() (string, error) {
	vGg, err := latestGoge()
	if err != nil {
		return "", err
	}
	return filepath.Join(vGg, "plugins/pkg/extensions"), nil
}

// 获取最新的goge
func latestGoge() (string, error) {
	ext := path.Join(gopath(), "pkg/mod/"+commonPath)
	files, err := ioutil.ReadDir(ext)
	if err != nil {
		return "", err
	}
	if len(files) == 0 {
		return "", errors.New("not found kratos package")
	}

	//find latest package of goge
	var gogeList []string
	for _, v := range files {
		match, err := regexp.MatchString("^goge@", v.Name())
		if err != nil {
			fmt.Printf("not found goge pacakge,err %+v\n", err)
			return "", err
		}
		if match {
			gogeList = append(gogeList, v.Name())
		}
	}
	if len(gogeList) <= 0 {
		fmt.Println("gogeList is empty")
		return "", nil
	}
	return path.Join(ext, gogeList[len(gogeList)-1]), nil
}

func gopath() (gp string) {
	if runtime.GOOS == "windows" {
		return strings.Split(build.Default.GOPATH, ";")[0]
	}
	return strings.Split(build.Default.GOPATH, ":")[0]
}
